﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using DVK.SP2007.Constants;
using Microsoft.SharePoint;
using System.Text;

namespace DVK.SP2007
{
    /// <summary>
    /// Helper methods to manage the inbox and outbox items.
    /// </summary>
    public class InboxOutboxItemHelper
    {
        /// <summary>
        /// Creates the target URL for inbox and outbox DVK messages.
        /// </summary>
        /// <param name="folder">The folder.</param>
        /// <param name="filename">The filename.</param>
        /// <returns></returns>
        public static string CreateTargetUrl(SPWeb web, string folderUrl, string filename)
        {
            //File name Pattern: HHmm_ddyyyy_filename
            if (string.IsNullOrEmpty(filename))
            {
                filename = "NoTitle.dvkmessage";
            }

            filename = CleanFilenameFromInvalidCharacters(web, filename);

            string targetUrl = folderUrl + "/" + DateTime.Now.ToString(General.InboxOutboxFilenameDateTimePattern) + filename;
            if (!targetUrl.EndsWith(General.DDOCExtension, StringComparison.OrdinalIgnoreCase))
            {
                //if there is an extension, substitute it with the DDOC one
                //NOTE: This was not a good idea, since not always there's a extension and then it cuts from the last "."
                //if (targetUrl.LastIndexOf('.') > -1)
                //{
                //    targetUrl = targetUrl.Remove(targetUrl.LastIndexOf('.'));
                //}
                targetUrl += General.DDOCExtension;
            }
            return targetUrl;
        }

        /// <summary>
        /// Cleans the target URL from invalid characters.
        /// </summary>
        /// <param name="web">The SPWeb object.</param>
        /// <param name="filename">The filename.</param>
        /// <returns>Target url which is clean of invalid characters.</returns>
        private static string CleanFilenameFromInvalidCharacters(SPWeb web, string filename)
        {
            var settingsRepository = new DvkSettingsRepository(web);
            var filenameFilterSetting = settingsRepository.QuerySetting(Settings.FilenameFilter);

            if (string.IsNullOrEmpty(filenameFilterSetting))
                return filename;

            StringBuilder filteredFilename = new StringBuilder(filename.Length);
            string[] charactersToFilter = filenameFilterSetting.Trim().Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries);

            var charactersInFilename = filename.ToCharArray();
            foreach (var characterInFilename in charactersInFilename)
            {
                if (charactersToFilter.Contains(characterInFilename.ToString()))
                    continue;//Skip the invalid character

                filteredFilename.Append(characterInFilename);
            }
            return filteredFilename.ToString();
        }

        /// <summary>
        /// Determines whether [is valid target URL] [the specified target URL].
        /// </summary>
        /// <param name="targetUrl">The target URL.</param>
        /// <returns>
        /// 	<c>true</c> if [is valid target URL] [the specified target URL]; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsValidTargetUrl(string targetUrl)
        {
            return !IsTooLongTargetUrl(targetUrl) && !IsTooLongFileName(targetUrl);
        }

        /// <summary>
        /// Determines whether [is too long target URL] [the specified target URL].
        /// </summary>
        /// <param name="targetUrl">The target URL.</param>
        /// <returns>
        /// 	<c>true</c> if [is too long target URL] [the specified target URL]; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsTooLongTargetUrl(string targetUrl)
        {
            return targetUrl.Length >= General.MaxTargetUrlLength;
        }

        public static bool IsTooLongFileName(string targetUrl)
        {
            var fileName = targetUrl.Substring(targetUrl.LastIndexOf('/') + 1);
            return fileName.Length >= General.MaxTargetFileNameLength;
        }

        public static string CutTargetUrl(string targetUrl, int index)
        {
            var characterCountToCut = targetUrl.Length - General.MaxTargetUrlLength;
            //IF the target url isent longer than max length, then check for filename length 
            if (characterCountToCut <= 0)
            {
                characterCountToCut = targetUrl.Substring(targetUrl.LastIndexOf('/') + 1).Length - General.MaxTargetFileNameLength;
            }
            var lastBackslashIndex = targetUrl.LastIndexOf('/');
            if (characterCountToCut > 0 && lastBackslashIndex != -1)
            {
                //Start cuting - if index > 0, then we have index in use as well
                var filename = targetUrl.Substring(lastBackslashIndex + 1);

                //we can only cut from file name. IF we need to cut more or equal to the filename, then we cant cut.
                if (filename.Length >= characterCountToCut)
                {
                    var cuttingPoint = targetUrl.Length - characterCountToCut - General.DDOCExtension.Length;
                    var indexText = string.Empty;
                    if (index > 0)
                    {
                        cuttingPoint = cuttingPoint - (index.ToString().Length+1); //"_1"
                        indexText = "_" + index.ToString();
                    }
                    targetUrl = targetUrl.Remove(cuttingPoint) + indexText + General.DDOCExtension;
                }
                else
                {
                    throw new DvkException("InboxOutboxItemHelper->CutTargetUrl: Cannot cut filename, because the filename is not long enough to make target path acceptable. Make your folders with shorter name.");
                }
            }
            return targetUrl;
        }

        /// <summary>
        /// Removes the date time from target URL.
        /// </summary>
        /// <param name="targetUrl">The target URL.</param>
        /// <returns></returns>
        public static string RemoveDateTimeFromTargetUrl(string targetUrl)
        {
            string fileName = targetUrl.Substring(targetUrl.LastIndexOf('/') + 1);
            fileName = fileName.Remove(0, General.InboxOutboxFilenameDateTimePattern.Length);
            targetUrl = targetUrl.Substring(0, targetUrl.LastIndexOf('/') + 1) + fileName;
            return targetUrl;
        }

        /// <summary>
        /// Gets the valid target URL.
        /// </summary>
        /// <param name="web">The web.</param>
        /// <param name="list">The list.</param>
        /// <param name="fileName">Name of the file.</param>
        /// <returns></returns>
        public static string GetValidTargetUrl(SPWeb web, SPList list, string fileName)
        {
            //Step 1: Create targetUrl
            //File name Pattern: HHmm_ddyyyy_filename
            string targetUrl = InboxOutboxItemHelper.CreateTargetUrl(web, list.RootFolder.Url, fileName);
            int index = -1;

            //Step 2: Validate Url length and cut of invalid
            if (!InboxOutboxItemHelper.IsValidTargetUrl(targetUrl))
            {
                targetUrl = InboxOutboxItemHelper.CutTargetUrl(targetUrl, index);
            }

            try
            {
                //Step 3: Check if such file already exists
                //If there already exists a file with the same name use numerator in the end
                //Check if this targetUrl is already in use
                // -3 allows _12 (2 digit numerator)
                var fileNameWithoutExtension = targetUrl.Remove(targetUrl.LastIndexOf('.')).Substring(targetUrl.LastIndexOf('/') + 1);

                //TODO: Limitation - can find documents with indexes upto 99!
                SPQuery query = new SPQuery();
                query.Query =
                        @"<Where>
                          <Or>
                          <Or>
                          <Contains>
                             <FieldRef Name='FileLeafRef' />
                             <Value Type='File'>" + fileNameWithoutExtension + @"</Value>
                          </Contains>
                          <Contains>
                             <FieldRef Name='FileLeafRef' />
                             <Value Type='File'>" + fileNameWithoutExtension.Remove(fileNameWithoutExtension.Length - 2) + @"</Value>
                          </Contains>
                          </Or>
                          <Contains>
                             <FieldRef Name='FileLeafRef' />
                             <Value Type='File'>" + fileNameWithoutExtension.Remove(fileNameWithoutExtension.Length - 3) + @"</Value>
                          </Contains>
                        </Or>
                        </Where>
                        <OrderBy>
                            <FieldRef Name='Created' Ascending='False' />
                        </OrderBy>";
                query.RowLimit = 1;
                var items = list.GetItems(query);
                //Step 4: If found file then extract the last number
                //Should return the last file that was added with the same name
                if (items.Count > 0)
                {
                    SPListItem item = items[0];
                    //Get the last index in numerator
                    var possibleIndexNumber = item.Name.Remove(item.Name.LastIndexOf('.')).Substring(item.Name.LastIndexOf('_') + 1);

                    //If we cant parse, its not a number and probably no index has been used yet. Lets set it to 1.
                    if (!int.TryParse(possibleIndexNumber, out index))
                    {
                        index = 0;
                    }
                    targetUrl = targetUrl.Remove(targetUrl.LastIndexOf('.')) + "_" + (++index).ToString() + General.DDOCExtension;
                }
            }
            catch (Exception exc)
            {
                throw new DvkException("Failed to validate targetUrl. Couldnt query list for existing items with same targeturl.", exc);
            }

            //STEP 5: Check if numerator made the targeturl too long again. Cut if needed. Cannot cut before, because dont know the number. 
            //If there already exists a file with the same name use numerator in the end
            if (!InboxOutboxItemHelper.IsValidTargetUrl(targetUrl))
            {
                targetUrl = InboxOutboxItemHelper.CutTargetUrl(targetUrl, index);
            }
            return targetUrl;
        }
    }
}